package com.superb.common.database.utils;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.binding.MapperMethod;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: ajie
 * @CreateTime: 2024-05-23 12:48
 */
public class BeanMethodUtils {

    private static final Map<Class<?>, Map<String, List<List<Class>>>> MAPPER_CACHE = new ConcurrentHashMap<>();

    private static Class<?> getClass(String clazzName) throws ClassNotFoundException {
        return Class.forName(clazzName);
    }

    public static Method getMethod(String id, Object parameter) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        String clazzName = id.substring(0, id.lastIndexOf('.'));
        String methodName = id.substring(id.lastIndexOf('.') + 1);
        Class<?> objectClass = getClass(clazzName);

        return getMethod(objectClass, methodName, getParamArr(parameter));
    }

    /**
     * 获取 mybatis 中 mapper 接口的参数列表的参数值
     *
     * @param parameter
     * @return
     */
    private static Object[] getParamArr(Object parameter) {
        Object[] paramArr = null;
        String paramName = "param";
        int offset = 0;
        // mapper 接口中使用的是 paramMap, 传多个参数
        if (parameter instanceof MapperMethod.ParamMap) {
            Map map = ((Map) parameter);
            if (!map.isEmpty()) {
                // 初始化 param_arr
                // int size = (map.size() - 3) >> 1;
                int size = map.size() >> 1;
                paramArr = new Object[size];
                if (map.containsKey("param1")) {
                    paramName = "param";
                    offset = 0;
                } else if (map.containsKey("arg0")) {
                    paramName = "arg";
                    offset = 1;
                } else if (map.containsKey("arg1")) {
                    paramName = "arg";
                    offset = 0;
                }
                for (int i = 1; i <= size; i++) {
                    // mapper 接口中使用 param0 ~ paramN 命名参数
                    paramArr[i - 1] = map.get(paramName + (i - offset));
                }

            }
        } else if (parameter != null) {
            paramArr = new Object[1];
            paramArr[0] = parameter;
        }
        return paramArr;
    }

    /**
     * 通过方法重载获取具体方法
     *
     * @param clazz
     * @param mapperMethod
     * @param paramArr
     * @return
     * @throws NoSuchMethodException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static Method getMethod(Class<?> clazz, String mapperMethod, Object[] paramArr) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
        // 1、查 mapper 接口缓存
        if (!MAPPER_CACHE.containsKey(clazz)) {
            createMethodParameter(clazz);
        }
        // 2、返回相应 method
        if (ArrayUtils.isNotEmpty(paramArr)) {
            A:
            for (List<Class> paramList : MAPPER_CACHE.get(clazz).get(mapperMethod)) {
                if (!paramList.isEmpty()) {
                    if (paramList.size() != paramArr.length) {
                        continue A;
                    }
                    for (int i = 0; i < paramArr.length; i++) {
                        if (paramArr[i] != null) {
                            if (!compareClass(paramList.get(i), paramArr[i].getClass())) {
                                continue A;
                            }
                        }
                    }
                    // 获取目标方法(已经重载)
                    return clazz.getMethod(mapperMethod, paramList.toArray(new Class[paramList.size()]));
                }
            }
        }
        return clazz.getMethod(mapperMethod);
    }


    /**
     * 创建类的方法映射
     * Map<Class<?>, Map<String, List<List<Class>>>>
     * Map<类, Map<方法名, 方法重载List<参数类型List<Class>>>>
     * class下map是方法和方法重载映射
     * map下 key为方法名 list为重载方法参数列表     *
     *
     * @param clazz
     */
    private static void createMethodParameter(Class<?> clazz) {
        Map<String, List<List<Class>>> methodMap = new HashMap<>(16);
        for (Method method : clazz.getMethods()) {
            List<List<Class>> paramLists = methodMap.containsKey(method.getName()) ? methodMap.get(method.getName()) : new ArrayList<>();
            List<Class> paramClass = new ArrayList<Class>();
            for (Type type : method.getParameterTypes()) {
                paramClass.add((Class) type);
            }
            paramLists.add(paramClass);
            methodMap.put(method.getName(), paramLists);
        }
        MAPPER_CACHE.put(clazz, methodMap);
    }


    /**
     * class 比较
     */
    private static boolean compareClass(Class<?> returnType, Class<?> paramType) throws NoSuchFieldException, IllegalAccessException {
        if (returnType == paramType) {
            return true;
        } else if (returnType.isAssignableFrom(paramType)) {
            // 判断 paramType 是否为 returnType 子类或者实现类
            return true;
        }
        // 基本数据类型判断
        else if (returnType.isPrimitive()) {
            // paramType为包装类
            return returnType == paramType.getField("TYPE").get(null);
        } else if (paramType.isPrimitive()) {
            // returnType为包装类
            return paramType == returnType.getField("TYPE").get(null);
        }
        return false;
    }

}
